#ifndef SIMPLE_IO_SEEK_H
#define SIMPLE_IO_SEEK_H

#include <cstddef> // std::size_t
#include <variant> // std::variant std::monostate
#include <type_traits> // std::enable_if_t
#include <string> // std::string
#include <utility> // std::forward

#include "error.h" // error
#include "read_write_seek_common.h" // not_seekable
#include "handle.h" // unique_hanlde operation interface

namespace simple::io
{

	using seek_result = std::variant<std::monostate,
		not_seekable,
		error
	>;

	[[nodiscard]]
	seek_result seek(const unique_handle&, std::size_t) noexcept;

	template <operation allowed,
		std::enable_if_t<allowed && operation::read || allowed && operation::write>* = nullptr>
	seek_result seek(const interface<allowed>& i, std::size_t size) noexcept
	{ return seek(i.handle, size); }

	constexpr struct seek_f_t
	{
		template <typename... Args>
		[[nodiscard]] decltype(auto) operator()(Args&& ... args) const noexcept
		{ return simple::io::seek(std::forward<Args>(args)...); }
	} seek_f;

	[[nodiscard]] inline std::string get_message(const seek_f_t&)
	{ return "simple::io::seek"; }

	using size_result = std::variant<std::size_t,
		not_seekable,
		error
	>;

	[[nodiscard]]
	size_result size(const unique_handle&) noexcept;

	template <operation allowed,
		std::enable_if_t<allowed && operation::read || allowed && operation::write>* = nullptr>
	size_result size(const interface<allowed>& i) noexcept
	{ return size(i.handle); }

	constexpr struct size_f_t
	{
		template <typename... Args>
		[[nodiscard]] decltype(auto) operator()(Args&& ... args) const noexcept
		{ return simple::io::size(std::forward<Args>(args)...); }
	} size_f;

	[[nodiscard]] inline std::string get_message(const size_f_t&)
	{ return "simple::io::size"; }

} // namespace simple::io

#endif /* end of include guard */
